package com.truth.lock.template;

import com.truth.lock.LockInfo;
import com.truth.lock.configure.TruthLockProperties;
import com.truth.lock.enumeration.TruthLockType;
import com.truth.lock.exception.AcquireLockException;
import com.truth.lock.executor.TruthLockExecutor;
import com.truth.lock.factory.TruthExecutorBeanFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author truth
 */
@SuppressWarnings("all")
public class TruthLockTemplate implements LockTemplate {

    private final static Logger logger = LoggerFactory.getLogger(TruthLockTemplate.class);

    private final TruthLockProperties truthLockProperties;

    public TruthLockTemplate(TruthLockProperties truthLockProperties) {
        this.truthLockProperties = truthLockProperties;
    }

    @Override
    public LockInfo lock(String key, long acquireCount, long expire, long acquireTimeout, TruthLockType lockType) {
        TruthLockExecutor executor = TruthExecutorBeanFactory.getExecutor(lockType);
        if (Objects.isNull(executor)) {
            logger.error("lock type is not exist");
            throw new AcquireLockException("lock type is not exist");
        }
        //专门微RedisTemplate而来的
        String value = UUID.randomUUID().toString();
        int obtain = 0;
        long start = System.currentTimeMillis();
        try {
            do {
                obtain++;
                Object acquire = executor.acquire(key, value, expire, acquireTimeout);
                if (Objects.nonNull(acquire)) {
                    LockInfo lockInfo = new LockInfo();
                    lockInfo.setLockKey(key);
                    lockInfo.setLockValue(value);
                    lockInfo.setExpire(expire);
                    lockInfo.setAcquireCount(obtain);
                    lockInfo.setAcquireTimeout(acquireTimeout);
                    lockInfo.setLockInstance(acquire);
                    lockInfo.setExecutor(executor);
                    return lockInfo;
                }
                TimeUnit.MILLISECONDS.sleep(truthLockProperties.getRetryInterval());
            } while (System.currentTimeMillis() - start < acquireTimeout && obtain < acquireCount);
        } catch (InterruptedException e) {
            logger.error("acquire lock error", e);
        }
        return null;
    }

    @Override
    public boolean releaseLock(LockInfo lockInfo) {
        TruthLockExecutor executor = lockInfo.getExecutor();
        return executor.releaseLock(lockInfo.getLockKey(), lockInfo.getLockValue(), lockInfo.getLockInstance());
    }

}
